package com.sen.common.utils;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * SQL like的模糊查询模式, 把字符串加上查询模式.
 * 
 * @author sen
 */
public final class SqlLikePatterns {

	private static final Logger LOG = LoggerFactory.getLogger(SqlLikePatterns.class);
	
	private final String pattern;
	private boolean before;
	private boolean end;
	private boolean trim;
	private boolean omitEmpty;
	private String postfix = "LikeRange";

	private SqlLikePatterns(String pattern) {
		this.pattern = pattern;
	}

	/**
	 * 不添加任何模式的模糊查询.
	 */
	public static SqlLikePatterns none() {
		return new SqlLikePatterns("");
	}

	/**
	 * 百分比模式的模糊查询.
	 */
	public static SqlLikePatterns percent() {
		return new SqlLikePatterns("%");
	}

	/**
	 * 下划线模式的模糊查询.
	 */
	public static SqlLikePatterns underline() {
		return new SqlLikePatterns("_");
	}

	/**
	 * 在字符串前面附上Like查询模式.
	 */
	public SqlLikePatterns attachBefore() {
		this.before = true;
		return this;
	}

	/**
	 * 在字符串后面附上Like查询模式.
	 */
	public SqlLikePatterns attachEnd() {
		this.end = true;
		return this;
	}

	/**
	 * 在字符串两边附上Like查询模式.
	 */
	public SqlLikePatterns attachBoth() {
		this.before = true;
		this.end = true;
		return this;
	}

	/**
	 * 去除字符串两边的空格.
	 */
	public SqlLikePatterns trim() {
		this.trim = true;
		return this;
	}

	/**
	 * 忽略空字符.
	 */
	public SqlLikePatterns omitEmpty() {
		this.omitEmpty = true;
		return this;
	}

	/**
	 * 只运用于以该<code>postfix</code>后缀为变量名的字符串.
	 */
	public SqlLikePatterns withPostfix(String postfix) {
		this.postfix = postfix;
		return this;
	}

	/**
	 * 运用于所有变量名的字符串.
	 */
	public SqlLikePatterns withAllFields() {
		this.postfix = null;
		return this;
	}

	/**
	 * 将Like查询模式运用在该字符串上.
	 */
	public String on(String str) {
		if (null == str) {
			return null;
		}
		StringBuffer buffer = new StringBuffer();
		if (trim) {
			buffer.append(Trimmer.trim(str));
		} else {
			buffer.append(str);
		}
		if (omitEmpty && StringUtils.isBlank(buffer.toString())) {
			return buffer.toString();
		}
		if (before) {
			buffer.insert(0, pattern);
		}
		if (end) {
			buffer.append(pattern);
		}
		return buffer.toString();
	}

	/**
	 * 将Like查询模式运用于<code>Object</code>(JavaBean).
	 */
	public SqlLikePatterns on(Object obj) {
		if (null == obj) {
			return this;
		}
		Field[] fields = obj.getClass().getDeclaredFields();
		if (ArrayUtils.isEmpty(fields)) {
			return this;
		}
		for (Field field : fields) {
			if (isNotEndOfPostfix(field.getName())) {
				continue;
			}
			try {
				field.setAccessible(true);
				Object value = field.get(obj);
				if (isNotStringClass(value)) {
					continue;
				}
				field.set(obj, on(value.toString()));
			} catch (IllegalArgumentException e) {
				LOG.warn("sqlLikePatterns转化错误", e);
			} catch (IllegalAccessException e) {
				LOG.warn("sqlLikePatterns转化错误", e);
			}
		}
		return this;
	}

	private boolean isNotStringClass(Object value) {
		return null == value || value.getClass() != String.class;
	}

	private boolean isNotEndOfPostfix(String key) {
		if (null == postfix) {
			return false;
		}
		if (StringUtils.isBlank(key)) {
			return true;
		}
		return !key.endsWith(postfix);
	}

	/**
	 * 将Like查询模式运用于<code>Map</code>.
	 */
	@SuppressWarnings("unchecked")
	public SqlLikePatterns on(Map map) {
		if (MapUtils.isEmpty(map)) {
			return this;
		}
		Set keys = map.keySet();
		if (CollectionUtils.isEmpty(keys)) {
			return this;
		}
		for (Object key : keys) {
			if (isNotStringClass(key) || isNotEndOfPostfix(key.toString())) {
				continue;
			}
			Object value = map.get(key);
			if (isNotStringClass(value)) {
				continue;
			}
			map.put(key, on(value.toString()));
		}
		return this;
	}
}
